iT邦幫忙

2024 iThome 鐵人賽

DAY 7
0
Software Development

一起看無間道學EdgeDB系列 第 7

[Day07] - 介紹multi property及array

  • 分享至 

  • xImage
  •  

multi property

multi property是一個容易被忽略的功能,可以視為以EdgeDBSet為容器來收集各種primitive

考慮schema如下:

type User {
    multi sports: str;
}

User object中有一個multi sports property,為str型別,用來記錄其喜愛的運動。

接著我們新增一個User object,並將{"baseball", "swimming"}這個EdgeDBSet指定給multi sports property

insert User {
    sports:= {"baseball", "swimming"}    
};

新增元素

假設我們想將badminton加入multi sports property中,可以這麼寫:

update User
set {
    sports+= {"badminton"}
};

上面這個query利用+=來新增badminton,是一個快速將元素加入EdgeDBSet的寫法,可以想成in place地加入元素。

如果不習慣這樣的寫法,也可以寫成:

update User
set {
    sports:= .sports union {"badminton"}
};

留意這邊使用的是:=:=代表將.sports union {"badminton"}這個set operation的結果指定給multi sports property

移除元素

假設我們想將badminton自multi sports property移除,可以這麼寫:

update User
set {
    sports-= {"badminton"}
};

或是寫成:

update User
set {
    sports:= .sports except {"badminton"}
};

留意這邊使用的是:=:=代表將.sports except {"badminton"}這個set operation的結果指定給multi sports property

array

array是有序的collection type,與EdgeDBSet一樣,其內所含的object須為同一型別,且可以被索引(索引值由0開始)。例如:

select [1, 2, 3][0];
{1}

請注意,此query返回結果依然是EdgeDBSet

除了利用索引取得單個元素外,也可以使用切片,例如:

select [1, 2, 3][1:];
{[2, 3]}

切片邏輯與Python相似。舉例來說,當select array[from:to]

  • 若想包含第一個元素,則from可省略不寫,或是寫為0。
  • 若想包含最後一個元素,則to可省略不寫,或是寫為-1。
  • [from:to]代表取得from~to-1的元素,to這個位置的元素並不會被選取。

考慮schema如下:

type User {
    sports: array<str>;
}

接著我們新增一個User object,並將["baseball", "swimming"]這個array指定給sports property

insert User {
    sports:= ["baseball", "swimming"]    
};

新增元素

假設我們想將badminton加入sports property中,可以這麼寫:

update User
set {
    sports:= .sports ++ ["badminton"]
};

請留意這邊我們需要使用:=++。EdgeDB在設計上,將array視為immutable,所以我們其實無法修改array,只能夠於每次需要更新array時,重新建立一個,然候指定給原先的property

移除元素

假設我們想將badminton自sports property移除,可以這麼寫:

update User
set {
    sports:= (
        with idx:= find(.sports, "badminton")
        select .sports[:idx] ++ .sports[idx+1:]
    )
};

這邊我們利用find先找出badminton所在的索引值,接著再手動重新建立一個排除badminton的array,指定給sports property

由於找不到元素時,find會回傳-1,上面的query將因此產生錯誤的答案。此時,我們可以透過if-else來幫忙,當想要移除的元素不在array中時,我們只是將原先的array再一次指定回sports property

update User
set {
    sports:= (
        with idx:= find(.sports, "badminton")
        select .sports[:idx] ++ .sports[idx+1:] if idx != -1 else .sports
    )
};

如果覺得上面邏輯有點亂的話,可以試著在with區塊中透過contains預先建立一個判斷邏輯,來檢視array中是否有該元素:

update User
set {
    sports:= (
        with is_in:= contains(.sports, "badminton"),
             idx:= find(.sports, "swimming")
        select .sports[:idx] ++ .sports[idx+1:] if is_in else .sports
    )
};

multi property vs array

至於何時該選擇multi property而何時又該選擇array呢?根據David MacLeody在Easy EdgeDB第八章所建議的:

  • 當想要收集的元素數量比較大時,使用multi property;反之則使用array
  • 當想要使用施加constraint或針對其使用index on時,使用multi property
  • 當想維持元素的順序或使用索引及切片等功能時,使用array

由於使用array需要於每次update重新生成一個array,操作起來比較麻煩。所以我個人會傾向將array使用在元素順序重要,且更新時只需不斷使用++將新元素添加到array的情況。

EdgeDBSetarray的互相轉換

array_unpack()

array_unpack()可以將array轉變為EdgeDBSet

select array_unpack([2, 3, 5]);
{3, 2, 5}

這邊需留意由array轉變為EdgeDBSet時,順序有可能會打亂(記得EdgeDBSet為無序的嗎?)。

array_agg()

array_agg()可以將EdgeDBSet轉變為array

select array_agg({2, 3, 5});
{[2, 3, 5]}

這邊需留意由EdgeDBSet轉變為array時,有兩種情況可以將順序將會保留在array中:

  • 第一種是如果該EdgeDBSet為顯性寫出來的型態,如{2, 3, 5}
  • 第二種為使用order by時。

舉例來說,考慮schema如下:

type User {
    required name: str;
}

接著執行下面query,生成三個User object

insert User {name:= "Jeff"};
insert User {name:= "Tom"};
insert User {name:="Cathy"};

此時下面query因為使用了order by,所以array內元素將會依照字母順序排好:

select array_agg(User.name order by User.name);
{['Cathy', 'Jeff', 'Tom']}

上一篇
[Day06] - 如何delete
下一篇
[Day08] - 介紹tuple、range及multirange
系列文
一起看無間道學EdgeDB30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言